package com.dbpms.pj.common.aspect;

import com.dbpms.pj.common.annotation.RequiredLog;
import com.dbpms.pj.common.utils.IPUtils;
import com.dbpms.pj.sys.pojo.SysLog;
import com.dbpms.pj.sys.service.SysLogService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;

/**
 * @Author: Song Yu
 * @Date: 2020/10/17 14:12
 */

/**
 * @Aspect 注解描述的对象为一个切面对象,在切面对象中定义
 * 1)切入点(Pointcut):织入扩展功能的一些连接点的集合
 * 2)通知方法(Advice):封装了扩展逻辑的方法
 */
@Slf4j
@Aspect
@Component
public class SysLogAspect {
    //通过Pointcut定义一个切入点,@annotation方式为定义切入点的一种方式,
    //在这里表示业务对象中由com.cy.pj.common.annotation.RequiredLog注解描述的方法为一些切入点方法
    @Pointcut("@annotation(com.dbpms.pj.common.annotation.RequiredLog)")
    public void doLog(){}//doLog方法仅仅是@Pointcut注解的一个载体,方法体内不需要写任何内容

    /**
     * @Around 注解描述的方法可以在目标方法执行之前和之后做功能扩展
     * @param joinPoint 封装了目标方法信息的一个对象(连接点对象)
     * @return 目标方法的执行结果
     * @throws Throwable
     */


    @Around("doLog()")
    public Object doAround(ProceedingJoinPoint joinPoint)throws Throwable{
        try {
            long t1 = System.currentTimeMillis();
            Object result = joinPoint.proceed();//去调用目标方法,其返回值为目标方法返回值
            long t2 = System.currentTimeMillis();
            //将正常的用户行为写入数据库
            saveSysLog(joinPoint, (t2 - t1));
            return result;
        }catch(Exception e){
            log.error("msg:"+e);
        }
        return null;
    }

    @Autowired
    private SysLogService sysLogService;

    private void saveSysLog(ProceedingJoinPoint joinPoint,long time) throws NoSuchMethodException, JsonProcessingException {
        //1.获取用户行为日志
        //1.1拿到目标对象的类
        //joinPoint.getTarget()是得到类的对象
        //joinPoint.getTarget().getClass()是得到类
        Class<?> targetClass = joinPoint.getTarget().getClass();
        //1.2拿到目标方法的全部信息(返回值类型，全包名，类名，方法名，参数列表)
        MethodSignature s = (MethodSignature)joinPoint.getSignature();

        //1.3拿到被注解标注的方法(需要获取类中的方法名和参数列表)
        Method targetMethod = targetClass.getDeclaredMethod(s.getName(),s.getParameterTypes());
        //1.4从拿到的方法中得到注解
        RequiredLog requiredLog = targetMethod.getAnnotation(RequiredLog.class);
        //1.5从注解中得到操作名
        String operation = requiredLog.value();
        //2.封装日志信息
        SysLog entity=new SysLog();
        entity.setUsername("cgb");
        entity.setIp(IPUtils.getIpAddr());
        entity.setOperation(operation);
        entity.setMethod(targetClass.getName()+"."+targetMethod.getName());//类全名+方法名
//        entity.setParams(Arrays.toString(joinPoint.getArgs()));
        entity.setParams(new ObjectMapper().writeValueAsString(joinPoint.getArgs()));
        entity.setTime(time);
        entity.setCreateDate(new Date());
        //3.保存用户行为值
        sysLogService.saveObject(entity);
    }
}
